//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES OR INDEMNITIES.
//
//
// (C) Copyright 2006 Marvell International Ltd.
// All Rights Reserved
//

#include <updateappui.h>
#include <monahans.h>
#include <ceddk.h>

#include "DispDrvr.h"
#include "xllp_defs.h"

#include "xllp_lcd.h"
#include "xllp_ost.h"
#include "lcd_defs.h"
#include "xllp_lcd_plat.h"
#include "args.h"
#include "ebootcfg.h"
#include "ioctl_cfg.h"
#include "xllp_gpio_plat.h"
#include "xllp_mfp_plat.h"

//------------------------------------------------------------------------------
#define DRAW_ULDRUI_TEST_IMAGE 0
#define FRAME_BUFFER_BASE_PHYSICAL FRAME_BUFFER_0_BASE_PHYSICAL

//------------------------------------------------------------------------------
XLLP_LCD_T XllpLCD;
XLLP_STATUS_T status;

BOOL gDrawCursorFlag = FALSE;
BOOL gInPowerHandler = FALSE;
BOOL bDoRotation     = FALSE;
BOOL g_DisplayIsInitialized = FALSE;

CRITICAL_SECTION displayMutex;
CRITICAL_SECTION frameDescriptorMutex;

int DispDrvr_cxScreen;
int DispDrvr_cyScreen;
int DispDrvr_cdwStride;
int activeFrameBuffer=0;
unsigned int gframeBufferSize = 0;

PBYTE gDibBuffer        = NULL;     // pointer to first byte of composition buffer
PBYTE gFrameBuffer      = NULL;     // pointer to first byte of screen memory
PBYTE gBlankFrameBuffer = NULL;     // pointer to first byte of screen memory
RECT  gCursorRect;
UINT  nDisplayType;

DWORD g_DisplayBasePhysical = 0;
DWORD g_DisplayBaseVirtual = 0;

volatile XLLP_LCD_REGISTERS_T * v_pLcdRegs         = NULL;
volatile XLLP_CLKMGR_T        * v_pClkRegs         = NULL;
volatile XLLP_GPIO_T          * v_pGPIORegs        = NULL;
volatile unsigned int         * v_pMFPRegs         = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorCh0fd1       = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorCh0fd2       = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorPalette      = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorCh2_YCbCr_Y  = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorCh3_YCbCr_Cb = NULL;
LCD_FRAME_DESCRIPTOR * frameDescriptorCh4_YCbCr_Cr = NULL;
XLLP_UINT32_T        * v_pPaletteBuffer            = NULL;
volatile unsigned int         * v_pOSTRegs         = NULL;
volatile	XLLP_VUINT32_T *v_SSPRegs		= NULL; 

//------------------------------------------------------------------------------
void Cleanup();
BOOL MapVirtualAddress();
extern XLLP_STATUS_T XllpLCDInit(P_XLLP_LCD_T pXllpLCD);

#ifdef __cplusplus
extern "C" {
#endif

extern BOOL VirtualSetAttributes(LPVOID lpvAddress, DWORD cbSize, DWORD dwNewFlags, DWORD dwMask, LPDWORD lpdwOldFlags);
extern void msWait(unsigned int);

#ifdef __cplusplus
}
#endif

#if DRAW_ULDRUI_TEST_IMAGE

#define abs(x) ((x)>0?(x):-(x))
void vline(int x1, int y1, int y2, __int16 c);
void hline(int x1, int y1, int x2,__int16 c);
void line(int ox1, int oy1, int ox2, int oy2, __int16 c);

#endif // DRAW_ULDRUI_TEST_IMAGE

//------------------------------------------------------------------------------
//
//  Global:  dpCurSettings
//
//  Set debug zones names and initial setting for driver
//
#ifdef DEBUG
DBGPARAM dpCurSettings = {
    _T("ULDRUI"),
    {
        _T("Error"), _T("Warning"), _T("Init"), _T("Function"),
        _T("Comments"), _T(""), _T(""), _T(""),
        _T(""), _T(""), _T(""), _T(""),
        _T(""), _T(""), _T(""), _T("")
    },
    0x5
};
#endif // Debug

//------------------------------------------------------------------------------

// Copied from Sa2video.cpp. 
void GetFrameBufferMemory() 
{
    DMA_ADAPTER_OBJECT Adapter;
    PHYSICAL_ADDRESS   PhysAddr;

    // the global pointer to the reserved memory region for the display... mapped into the
    // shared memory region below...
    g_DisplayBaseVirtual = (DWORD)HalAllocateCommonBuffer(&Adapter, DISPLAY_SDRAM_REGION_SIZE, &PhysAddr, FALSE);	
    g_DisplayBasePhysical = PhysAddr.LowPart;

    // map shared virtual memory, and set the virtual ptr to that address rather than local process
    // address; from this point on, all processes will be able to access the
    // display region using g_DisplayBaseVirtual
    g_DisplayBaseVirtual = (DWORD)VirtualAlloc(NULL, DISPLAY_SDRAM_REGION_SIZE, MEM_RESERVE, PAGE_NOACCESS);

    if(!VirtualCopy((LPVOID)g_DisplayBaseVirtual, (LPVOID)((unsigned long)g_DisplayBasePhysical >> 8), DISPLAY_SDRAM_REGION_SIZE, (PAGE_READWRITE | PAGE_NOCACHE | PAGE_PHYSICAL)))
    {
        ERRORMSG( 1, ( TEXT( "CRITICAL ERROR: display SDRAM region allocation failed" ) ) );
    }

    return;
}
//  end

static void panel_select()
{
#if 1	
	DWORD cfg;
	DWORD out_size;
	BSP_ARGS args;
	KernelIoControl(IOCTL_GET_BSP_ARGS, NULL, 0, 
					&args, sizeof(BSP_ARGS), &out_size);

	cfg=args.eBootCFG.drvCfg[CFG_LCD_PANEL];
#endif

	XllpLCDInitTopoly(&XllpLCD, cfg);
	
}

//------------------------------------------------------------------------------

VOID InitBacklight()
{
/* TODO: Add backlight suppor here - Littleton */
#if 0
    // Configure Multifunction Pins
    XllpMfpSetAfDs( (P_XLLP_VUINT32_T) v_pMFPRegs, XLLP_MFP_BACKLIGHT_PWM_OFFSET, XLLP_MFP_ALT_FN_0, XLLP_MFP_DS_04X);

    // Configure Backlight GPIO Direction
    XllpGpioSetDirection( (P_XLLP_GPIO_T) v_pGPIORegs, XLLP_GPIO_BACKLIGHT, XLLP_GPIO_DIRECTION_OUT);

    // Start with Backlight off
    XllpGpioSetOutputLevel( (P_XLLP_GPIO_T) v_pGPIORegs, XLLP_GPIO_BACKLIGHT, XLLP_LO);
#endif	
}

VOID BacklightOn()
{
/* TODO: Add backlight suppor here - Littleton */
#if 0
    // Turn Backlight on
    XllpGpioSetOutputLevel( (P_XLLP_GPIO_T) v_pGPIORegs, XLLP_GPIO_BACKLIGHT, XLLP_HI);
#endif
}

VOID BacklightOff()
{
/* TODO: Add backlight suppor here - Littleton */
#if 0
    // Turn Backlight off
    XllpGpioSetOutputLevel( (P_XLLP_GPIO_T) v_pGPIORegs, XLLP_GPIO_BACKLIGHT, XLLP_LO);
#endif
}

//------------------------------------------------------------------------------

BOOL InitDisplay( DWORD width, DWORD height, DWORD bpp, DWORD orientation)
{

   if( g_DisplayIsInitialized ) return (TRUE);

#if DRAW_ULDRUI_TEST_IMAGE
    __int16 c = 0;
    int i = 0;
    int x1,x2,y1,y2;
#endif // DRAW_ULDRUI_TEST_IMAGE

    // Not used:
    // gframeBufferSize = bpp / 8 * width * height;

    // Allocate some DMA-able space for the LCD
    GetFrameBufferMemory();

    // Map registers, the frame buffer, and frame descriptors from Kernel mode virtual address 
    // into our user mode virtual address space
    if (!MapVirtualAddress())
    {
        return (FALSE);
    }

    // Get access to the MFP database
    HANDLE m_MfpRMDbHandle;
    BOOL bFirstInit;

    // Claim memory-mapped file for sharing MfpRmDb information.
    m_MfpRMDbHandle = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE,
                                        0, XLLP_MFP_RM_DB_SIZE, MFPRMDB_SHARED_FILENAME );

    if (NULL == m_MfpRMDbHandle)       
    {
        NKDbgPrintfW(TEXT("ULDR Display driver Fail to CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n"));
        return FALSE; //if failed
    } else {
        // We need to tell if it's first or sequent call of  CreateFileMapping(MFPRMDB_SHARED_FILENAME)
        if (ERROR_ALREADY_EXISTS != GetLastError()) {
            // The first initialization of this memory-mapped file.
            RETAILMSG(1, (TEXT("ULDR Display driver First call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));           
            bFirstInit = TRUE;        
        } else {
            // Memory-mapped file already existed.
            RETAILMSG(1, (TEXT("ULDR Display driver Sequent call of CreateFileMapping(MFPRMDB_SHARED_FILENAME)! \r\n")));
            bFirstInit = FALSE;
        }

        // Retrieve the local handle of the global shared memory-mapped file
        XllpLCD.pMfpRmDb = (P_XLLP_MFP_RM_DB_ID_T) MapViewOfFile(m_MfpRMDbHandle, FILE_MAP_ALL_ACCESS, 0, 0, 0 );
        if (NULL == XllpLCD.pMfpRmDb )
        {
            NKDbgPrintfW(TEXT("ULDR Display driver Fail to MapViewOfFile()!\r\n"));
            return FALSE; //if failed
        }

        if (TRUE == bFirstInit) {
            // Need to Initialize RM 
            if (XLLP_STATUS_SUCCESS != XllpMfpResourceManagerInit(XllpLCD.pMfpRmDb)) {
                NKDbgPrintfW(TEXT("ULDR Display driver Fail to XllpMfpResourceManagerInit()!\r\n"));
                return FALSE; //if failed
            }
        }
        
    }// end if (NULL == m_MfpRMDbHandle)


    XllpLCD.SSP = (XLLP_VUINT32_T *) v_SSPRegs;
    XllpLCD.GPIO = (XLLP_VUINT32_T *) v_pGPIORegs;
    XllpLCD.CLKMan = (XLLP_VUINT32_T *) v_pClkRegs;
    XllpLCD.LCDC = (XLLP_VUINT32_T *) v_pLcdRegs;
    XllpLCD.OST = (XLLP_VUINT32_T *) v_pOSTRegs;
    XllpLCD.MFP = (XLLP_VUINT32_T *) v_pMFPRegs;

    XllpLCD.BPP = BPP_16;
    XllpLCD.PixelDataFormat = PDFOR_00; //with overlays enabled use PDFOR_11 for 16bpp

    XllpLCD._FRAME_BUFFER_BASE_PHYSICAL = FRAME_BUFFER_BASE_PHYSICAL;
    XllpLCD._PALETTE_BUFFER_BASE_PHYSICAL = PALETTE_BUFFER_BASE_PHYSICAL;
    XllpLCD._DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_PHYSICAL = DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_PHYSICAL;
    XllpLCD._DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL = DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_PHYSICAL;
    XllpLCD._PALETTE_FRAME_DESCRIPTOR_BASE_PHYSICAL = PALETTE_FRAME_DESCRIPTOR_BASE_PHYSICAL;

    XllpLCD.frameDescriptorCh0fd1 = frameDescriptorCh0fd1;
    XllpLCD.frameDescriptorCh0fd2 = frameDescriptorCh0fd2;
    XllpLCD.frameDescriptorPalette = frameDescriptorPalette;

    panel_select();

    // Initialize the LCD Controller
    XllpLCDInit(&XllpLCD);

    NKDbgPrintfW(TEXT("\r\nUldrUI Display Driver Initialization Complete\r\n"));

#if DRAW_ULDRUI_TEST_IMAGE
    // Draw a pattern on the screen to show we're up.
    c = 0x1f;
    y1 = 240;
    x2 = 320;
    for (x1=80,y2=240;y2>0;x1+=10,y2-=10)
    {

        line(x1,y1,x2,y2,c);

    }

    y1 = 240;
    x2 = 320;
    for (x1=80,y2=240;y2<480;x1+=10,y2+=10)
    {

        line(x1,y1,x2,y2,c);

    }

    y1 = 240;
    x2 = 320;
    for (x1=560,y2=240;y2>0;x1-=10,y2-=10)
    {

        line(x1,y1,x2,y2,c);

    }

    y1 = 240;
    x2 = 320;
    for (x1=560,y2=240;y2<480;x1-=10,y2+=10)
    {

        line(x1,y1,x2,y2,c);

    }
#endif // DRAW_ULDRUI_TEST_IMAGE

   // Take care of backlight
   InitBacklight();
   BacklightOn();

   g_DisplayIsInitialized = TRUE;

    return(TRUE);
}

//------------------------------------------------------------------------------

BOOL DeinitDisplay()
{
    if( !g_DisplayIsInitialized ) return (TRUE);

    BacklightOff();

    XllpLCDSuspend(&XllpLCD,Suspend_Graceful);

    //workaround for the failure of image update
    //check it later
    Cleanup();

    g_DisplayIsInitialized = FALSE;

    // Done
    return(TRUE);
}

//------------------------------------------------------------------------------

// Free all the global memory resources
void Cleanup(void)
{
    DMA_ADAPTER_OBJECT Adapter;
    PHYSICAL_ADDRESS   PhysAddr;

    if (v_pLcdRegs)
    {
        VirtualFree((PVOID)v_pLcdRegs,0,MEM_RELEASE);
        v_pLcdRegs = NULL;
    }

    if (v_pClkRegs)
    {
        VirtualFree((PVOID)v_pClkRegs,0,MEM_RELEASE);
        v_pLcdRegs = NULL;
    }

    if (v_pGPIORegs)
    {
        VirtualFree((PVOID)v_pGPIORegs,0,MEM_RELEASE);
        v_pGPIORegs = NULL;
    }

    if (gFrameBuffer)
    {
        VirtualFree((PVOID)gFrameBuffer,0,MEM_RELEASE);
        gFrameBuffer = NULL;
    }

    if (v_pMFPRegs)
    {
        VirtualFree((PVOID)v_pMFPRegs, 0, MEM_RELEASE);
        v_pMFPRegs = NULL;
    }

    if (v_SSPRegs)
    {
        VirtualFree((PVOID)v_SSPRegs, 0, MEM_RELEASE);
        v_SSPRegs = NULL;
    }
	
    if (frameDescriptorCh2_YCbCr_Y)
    {
        VirtualFree((PVOID)frameDescriptorCh2_YCbCr_Y,0,MEM_RELEASE);
        frameDescriptorCh2_YCbCr_Y = NULL;
    }

    if (frameDescriptorCh3_YCbCr_Cb)
    {
        VirtualFree((PVOID)frameDescriptorCh3_YCbCr_Cb,0,MEM_RELEASE);
        frameDescriptorCh3_YCbCr_Cb = NULL;
    }

    if (frameDescriptorCh4_YCbCr_Cr)
    {
        VirtualFree((PVOID)frameDescriptorCh4_YCbCr_Cr,0,MEM_RELEASE);
        frameDescriptorCh4_YCbCr_Cr = NULL;
    }

    Adapter.ObjectSize    = sizeof (DMA_ADAPTER_OBJECT);
    Adapter.InterfaceType = Internal;
    Adapter.BusNumber     = 0;

    PhysAddr.HighPart = 0;
    PhysAddr.LowPart  = g_DisplayBasePhysical;

    HalFreeCommonBuffer(&Adapter, DISPLAY_SDRAM_REGION_SIZE, PhysAddr, (void *)g_DisplayBaseVirtual, FALSE);
}


//------------------------------------------------------------------------------


BOOL MapVirtualAddress()
{
    PHYSICAL_ADDRESS RegPA;

    if (v_pOSTRegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_OST;
        v_pOSTRegs = (volatile unsigned int *) MmMapIoSpace(RegPA, sizeof(XLLP_OST_T), FALSE);
    }
    
    if (!v_pOSTRegs)
    {
        Cleanup();
        return(FALSE);
    } 
    if (v_pLcdRegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_LCD;
        v_pLcdRegs = (volatile XLLP_LCD_REGISTERS_T *) MmMapIoSpace(RegPA, sizeof(XLLP_LCD_REGISTERS_T), FALSE);
    }
    
    if (!v_pLcdRegs)
    {
        Cleanup();
        return(FALSE);
    }
    
    if (v_pClkRegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_CLKMGR;
        v_pClkRegs = (volatile XLLP_CLKMGR_T *) MmMapIoSpace(RegPA, sizeof(XLLP_CLKMGR_T), FALSE);
    }
    
    if (!v_pClkRegs)
    {
        Cleanup();
        return(FALSE);
    }

    if (v_pGPIORegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_GPIO;
        v_pGPIORegs = (volatile XLLP_GPIO_T *) MmMapIoSpace(RegPA, sizeof(XLLP_GPIO_T), FALSE);
    }

    if (!v_pGPIORegs)
    {
        Cleanup();
        return(FALSE);
    }

    if (v_pMFPRegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_MFP;
        v_pMFPRegs = (volatile unsigned int *) MmMapIoSpace(RegPA, 0x700, FALSE);
    }

    if (!v_pMFPRegs)
    {
        Cleanup();
        return (FALSE);
    }

    if (v_SSPRegs == NULL)
    {
        RegPA.QuadPart = MONAHANS_BASE_REG_PA_SSP2;
        v_SSPRegs = (volatile XLLP_VUINT32_T *) MmMapIoSpace(RegPA, 0x700, FALSE);
    }

    if (!v_SSPRegs)
    {
        Cleanup();
        return (FALSE);
    }


    frameDescriptorCh0fd1  = (LCD_FRAME_DESCRIPTOR *)(DMA_CHANNEL_0_FRAME_DESCRIPTOR_BASE_VIRTUAL);	// = g_DisplayBaseVirtual
    frameDescriptorCh0fd2  = (LCD_FRAME_DESCRIPTOR *)(DMA_CHANNEL_0_ALT_FRAME_DESCRIPTOR_BASE_VIRTUAL);
    frameDescriptorPalette = (LCD_FRAME_DESCRIPTOR *)(PALETTE_FRAME_DESCRIPTOR_BASE_VIRTUAL);
    v_pPaletteBuffer       = (XLLP_UINT32_T *)(PALETTE_BUFFER_BASE_VIRTUAL);

    gFrameBuffer           = (PBYTE)FRAME_BUFFER_0_BASE_VIRTUAL;

    return(TRUE);
}

//------------------------------------------------------------------------------

BOOL BltRect(RECT rc, DWORD stride, VOID *pBuffer)
{
    volatile USHORT *pFrame = (USHORT *)(FRAME_BUFFER_0_BASE_VIRTUAL);
    DWORD screenWidth = XllpLCD.panel->any.width;
    USHORT *pData = (USHORT*)pBuffer;
    LONG xs, ys, xt, yt;

    // Convert stride from bytes to words
    stride /= sizeof(USHORT);

    for (yt = rc.top, ys = 0; yt < rc.bottom; yt++, ys++)
    {
        for (xt = rc.left, xs = 0; xt < rc.right; xt++, xs++)
        {
            pFrame[yt * screenWidth + xt] = pData[ys * stride + xs];
        }
    }

    return(TRUE);
}

#if DRAW_ULDRUI_TEST_IMAGE
void line(int ox1, int oy1, int ox2, int oy2, __int16 c)
{

    int i,j;
    int idy;
    int idx;
    int off;
    int x,y;
    int x1, x2, y1, y2;

    __int16 *fbp;
    fbp=(__int16 *)FRAME_BUFFER_0_BASE_VIRTUAL;

    if (abs(ox2-ox1) > abs(oy2 - oy1))
    {
        if (ox2<ox1)
        {
            x1 = ox2;
            x2 = ox1;
            y1 = oy2;
            y2 = oy1;
        } else
        {
            x1 = ox1;
            x2 = ox2;
            y1 = oy1;
            y2 = oy2;
        }
    } else
    {
        if (oy2<oy1)
        {
            x1 = ox2;
            x2 = ox1;
            y1 = oy2;
            y2 = oy1;
        } else
        {
            x1 = ox1;
            x2 = ox2;
            y1 = oy1;
            y2 = oy2;
        }
    }

    if (x2-x1 > y2 - y1)
    {
        if (y2 == y1)
        {
            idx = 0; //draw horiz line
            hline(x1, y1, x2, c);
        } else
        {
            idy = (65536 * (y2 - y1)) / (x2 - x1);

            off = y1 * 640;
            y = y1 << 16;
            for (i=x1;i<x2;i++)
            {
                *(fbp+i+(y>>16)*640) = (__int16)c;
                y += idy;
            }
        }
    } else
    {
        if (x2 == x1)
        {
            idx = 0; //vertical
            vline(x1, y1, y2, c);
        } else
        {
            idx = (65536 * (x2 - x1)) / (y2 - y1 );
            off = y1 * 640;
            x= x1 << 16;
            for (j=y1;j<y2;j++)
            {
                *(fbp+(x>>16)+off) = (__int16)c;
                x += idx;
                off+=640;
            }
        }
    }
}

void hline(int x1, int y1, int x2,__int16 c)
{
    int i,j;
    __int16 *fbp;

    fbp=(__int16 *)FRAME_BUFFER_BASE_PHYSICAL;
    i = y1 * 640 + x1;
    for (j = i; j < x2+i; j++)
    {
        *(fbp+j) = (__int16)c;
    }

}

void vline(int x1, int y1, int y2, __int16 c)
{
    int i,j,k;
    __int16 *fbp;

    fbp=(__int16 *)FRAME_BUFFER_BASE_PHYSICAL;
    i = y1 * 640 + x1;
    k = 640;
    for (j = 0; j < y2; j++)
    {
        *(fbp+i+k*j) = (__int16)c;
    }

}

#endif // DRAW_ULDRUI_TEST_IMAGE
